package logica.robots;

import java.awt.Color;
import java.util.*;

import logica.Nivel;
import logica.Orientacion;
import logica.coleccionables.Coleccionable;
import logica.pisos.Piso;
import logica.premios.Premio;

/**
 * Clase Robot: modela un robot, un robot es capaz de moverse por el nivel recolectando coleccionables y premios hasta 
 * llegar al piso final del nivel. En el transcurso este va usando su combustible y puede reponerlo mediante los packs de 
 * combustibles que se encuentran como premio en el nivel, tambien aunmenta sus puntos mediantes la recoleccion de packs
 * de puntos. 
 * Su orientación se codifica según la clase Orientacion. 
 * @author Amorosi, Javier (94528)
 * @author Antiman, Nehuen (102440)
 */
public abstract class Robot {
	
	protected int combustible, puntos, orientacion;
	protected Pincel pincel;
	protected Stack<Coleccionable> baul;
	
	/**
	 * Constructor de la clase Robot.
	 * El combustible se incializa en 0 dejando la resposabilidad a la clase que extienda Robot de iniciar el combustible
	 * según corresponda, el color del pincel se encuentra desactivado por defecto,
	 * los puntos en 0, y con orientación hacia arriba(0), el baúl de coleccionables se encuentra vacio. 
	 */
	public Robot(){
		orientacion =0;
		combustible = 0;
		puntos = 0;
		pincel = null;
		baul = new Stack<Coleccionable>();
	}
	
	/**
	 * Retorna el color del pincel, si este se encuentra activado, null caso contrario. 
	 * @return color del pincel, si este se encuentra activado, null caso contrario. 
	 */
	public Color getColorPincel(){
		Color toR=null;
		if (pincel != null && pincel.estaActivado())
			toR = pincel.getColor();
		return toR;		
	}
	
	/**
	 * Retorna la cantidad de puntos acumulados por el robot. 
	 * @return puntos acumulados por el robot.
	 */
	public int getPuntos(){
		return puntos;
	}
	
	/**
	 * Retorna el combustible disponible en el tanque del robot. 
	 * @return combustible disponible en el tanque del robot. 
	 */
	public int getCombustible(){
		return combustible;	
	}
	
	/**
	 * Retorna un número entero representando la orientación del robot.
	 * arriba=0.
	 * derecha=1.
	 * abajo=2.
	 * izquierda=3.
	 * @return orientación del robot.
	 */
	public int getOrientacion(){
		return orientacion;
	}
	
	/**
	 * Retorna el último coleccionable recolectado por el robot, si no ha recolectado ninguno se retorna null.
	 * @return último coleccionable recolectado por el robot, si no ha recolectado ninguno se retorna null.
	 */
	public Coleccionable getUltimoColeccionable(){
		Coleccionable toR = null;
		if (!baul.isEmpty())
			toR = baul.peek();
		return toR;	
	}
	
	/**
	 * Suelta el último coleccionable recolectado por el robot. Retorna un valor booleano representando si se 
	 * soltó algún coleccionable o no.
	 * @return true si se soltó un coleccionable exitosamente o no. 
	 */
	public Coleccionable soltarColeccionable(){
		Coleccionable r=null;
		try{
			if (!baul.isEmpty())
				r=baul.pop();							
		}
		catch(EmptyStackException e){
			e.printStackTrace(); 
		}
		return r;
	}
	
	/**
	 *  Se activa el pincel del robot con el color indicado.
	 * @param color color con cual se activa el pincel. 
	 */
	public void activarPincel(Color color){
		if (pincel == null)
			pincel = new Pincel(color);	
		else
			pincel.setColor(color);
		pincel.activarPincel();
	}
	
	/**
	 * Se desactiva el pincel del robot.
	 */
	public void desactivarPincel(){
		if (pincel != null)
			pincel.desactivarPincel();
	}
	
	private void consumirCombustible(int consumo){
		combustible -= consumo;
	}
	
	/**
	 * El robot avanza consumiendo la cantidad de combustible indicada, esta cantidad depende de donde esté 
	 * transitando el robot.
	 * @param consumo cantidad de combustible requerida.
	 * @return true si el robot disponia del suficiente combustible, false caso contrario.
	 */
	public boolean avanzar(int consumo){
		boolean r=(combustible-consumo>=0);
		if (r)
			consumirCombustible(consumo);
		return r;
	}
	
	/**
	 * El robot gira consumiendo la cantidad de combustible indicada, esta cantidad depende de donde esté 
	 * girando el robot, gira hacia la orientación indicada. 
	 * Tener en cuenta que el robot solo gira 90 grados. Ejemplo: si el robot tiene orientacion hacia arriba(0), el 
	 * pasaje de una orientación hacia abajo(2) no tendría ningun efecto, aun asi el robot puede girar hacia la 
	 * izquierda(3) o a derecha(1). 
	 * @param o orientacion del giro.
	 * @param consumo cantidad de combustible requerida para un giro de 90 grados.
	 * @return true si el robot disponia del suficiente combustible y el giro para lograr la orientacion propuesta
	 * es de 90 grados, false caso contrario.
	 */
	public boolean girar(int o, int consumo){	
		boolean r= (o==Orientacion.DERECHA||o==Orientacion.IZQUIERDA) && (combustible-consumo>=0);
		if (r){
			consumirCombustible(consumo);
			if (o==Orientacion.DERECHA)
				orientacion=(orientacion+Orientacion.DERECHA)%4;
			else
				orientacion=(orientacion+Orientacion.IZQUIERDA)%4; 
		}
		return r;
	}
	
	/**
	 * El robot coloca en su baúl el coleccionable pasado.
	 * @param c coleccionable a colocar en el baúl.
	 */
	public void recolectarColeecionable(Coleccionable c){
		baul.push(c);
	}
	
	/**
	 * Recolecta el premio en indicado, adjudicanse el valor del premio segun corresponda.
	 * @param p Premio a recolectar.
	 */
	public abstract void recolectarPremio(Premio p);
	
	
	/**
	 * Indica se el pincel esta activado.
	 * @return true si el pincel esta activado, false caso contrario.
	 */
	public boolean estaPincelActivado(){
		boolean toR=false;
		if (pincel!=null)
			toR= pincel.estaActivado();
		return toR;
	}

	/**
	 * Método a implementar en clases derivadas, al tratarde de una "acción especial", 
	 * su comportamiento depende totalmente de la clase que lo implemente.
	 * @param nivel se le proporciona al robot el nivel para que pueda realizar su acción especial.
	 * @param pisoActual se le proporciona al robot el piso donde se encuentra para que pueda realizar su acción especial.
	 * @return true si la acción fue llevada con éxito. 
	 */
	public abstract boolean accionEspecial(Nivel nivel, Piso pisoActual);
	
	/**
	 * Retorna un string con representando los valores de sus atributos. 
	 */
	public String toString(){
		return "Elementos en el bául: "+baul.size()+" | Combustible: "+combustible+" | Puntos: "+puntos;
	}
}
